/************************************************************/
/*
	File: CrudUIGenerator.java

	CrudUIGenerator reads an service schema definition and
	generates a UI.

	Author: James Cowan
*/
/************************************************************/

package org.ems.crud;

import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;

import org.apache.log4j.Logger;
import org.ems.crud.model.Binding;
import org.ems.crud.model.BindingProperty;
import org.ems.crud.model.BindingPropertyType;
import org.ems.crud.model.CrudService;
import org.ems.crud.model.CrudServiceList;
import org.ems.generator.GeneratorBean;
import org.ems.generator.GeneratorException;

/************************************************************/

abstract public class CrudUIGenerator
{
	protected File				m_output;
	protected CrudServiceList	m_crudServiceList;
	
	public static final String	ADD = "Add";
	public static final String	ADD_BUTTON = "AddButton";
	public static final String	CANCEL = "Cancel";
	public static final String	CANCEL_BUTTON = "CancelButton";
	public static final String	EDIT = "Edit";
	public static final String	EDIT_BUTTON = "EditButton";
	public static final String	FILTERS = "Filters";
	public static final String	FILTER_LIST = "FilterList";
	public static final String	FIRST_BUTTON = "FirstButton";
	public static final String	FORM = "Form";
	public static final String	LAST_BUTTON = "LastButton";
	public static final String	NEXT_BUTTON = "NextButton";
	public static final String	OK = "OK";
	public static final String	OK_BUTTON = "OkButton";
	public static final String	PAGE = "Page";
	public static final String	PAGE_COUNT = "PageCount";
	public static final String	PAGE_OF = "Of";
	public static final String	PAGE_SIZE = "Page Size";
	public static final String	PAGE_SIZE_INPUT = "PageSize";
	public static final String	PAGE_SIZE_BUTTON = "PageSizeButton";
	public static final String	PAGE_SIZE_CHANGE = "Change";
	public static final String	PAGE_TOTAL = "PageTotal";
	public static final String	PREVIOUS_BUTTON = "PreviousButton";
	public static final String	REMOVE_FILTER = "Remove Filter";
	public static final String	REFRESH = "Refresh";
	public static final String	REFRESH_BUTTON = "RefreshButton";
	public static final String	REMOVE_FILTER_BUTTON = "RemoveFilterButton";
	public static final String	REMOVE = "Remove";
	public static final String	REMOVE_BUTTON = "DeleteButton";
	public static final String	RESET_SORT = "Reset Sort";
	public static final String	RESET_SORT_BUTTON = "ResetSortButton";
	public static final String	TABLE = "Table";
	public static final int		VERTICAL_SPACER = 35;
	
	static protected Logger		m_log = Logger.getLogger(CrudUIGenerator.class);
	
	public CrudUIGenerator(File output, CrudServiceList crudServiceList)
	{
		m_output = output;
		m_crudServiceList = crudServiceList;
	}

	/************************************************************/
	/* public methods */
	/************************************************************/
	/** generate */

	abstract public void generate() throws IOException, GeneratorException;
	
	/************************************************************/
	/* protected methods */
	/************************************************************/
	/** getBinding */
	
	protected String getBinding(BindingProperty property)
	{
	String join = property.getJoin();
	
		if (join.equals(""))
		{
			return property.getName();
		}
		
		return property.getName()+"."+join;
		
	} // getBinding
	
	/************************************************************/
	/** getBindingPropertyType */
	
	protected BindingPropertyType[] getBindingPropertyType(CrudService service)
	throws GeneratorException
	{
	String className = service.getClassName();
	String parentClassName = service.getParentClassName();
	GeneratorBean generatorBean = new GeneratorBean();
	PropertyDescriptor descriptors[] = generatorBean.getPropertyDescriptors(className, parentClassName);
	
		return getBindingPropertyType(service, descriptors);

	} // getBindingPropertyType
	
	/************************************************************/
	/** getBindingPropertyType */
	
	protected BindingPropertyType[] getBindingPropertyType(CrudService service, PropertyDescriptor descriptors[])
	throws GeneratorException
	{
	Binding binding = service.getBinding();
	BindingProperty bindingProperties[] = binding.getBindingProperty();
	HashMap<String, PropertyDescriptor> propertyMap = getPropertyMap(descriptors);
	HashMap<String, BindingProperty> bindingMap = new HashMap<String, BindingProperty>();
	ArrayList<BindingPropertyType> resultList = new ArrayList<BindingPropertyType>();
		
		// create map of properties
		
		for (int count=0; count<bindingProperties.length; count++)
		{
		BindingProperty bindingProperty = bindingProperties[count];
		String name = bindingProperty.getName();
			
			bindingMap.put(name, bindingProperty);
		}
		
		// add binding property
		
		for (int count=0; count<bindingProperties.length; count++)
		{
		BindingProperty bindingProperty = bindingProperties[count];
		String name = bindingProperty.getName();
		PropertyDescriptor descriptor = propertyMap.get(name);
		 
		 	// kludge to allow nested paths without validation
		 	
			if (descriptor == null)
			{
				if (name.indexOf(".") == 0)
				{
				String msg = "Invalid property: "+name+" in class: "+service.getClassName();
				
					throw new GeneratorException(msg);
				}
				else
				{
				GeneratorBean generatorBean = new GeneratorBean();
				
					descriptor = getNestedDescriptor(bindingProperty, name, propertyMap, generatorBean);
				}
			}
			
			BindingPropertyType bindingPropertyType = new BindingPropertyType(bindingProperty);
			
			bindingPropertyType.setPropertyDescriptor(descriptor);
			resultList.add(bindingPropertyType);
		}

		for (int count=0; count<descriptors.length; count++)
		{
		PropertyDescriptor descriptor = descriptors[count];
		String name = descriptor.getName();
		
			if (bindingMap.get(name) != null)
			{
				continue;
			}
			BindingPropertyType bindingPropertyType = new BindingPropertyType();
			
			bindingPropertyType.setTitle(name);
			bindingPropertyType.setName(name);
			bindingPropertyType.setPropertyDescriptor(descriptor);
			resultList.add(bindingPropertyType);
		}
		
		BindingPropertyType result[] = new BindingPropertyType[resultList.size()];
		
		resultList.toArray(result);
		
		return result;
		
	} // getBindingPropertyType
	
	/************************************************************/
	/** getNestedDescriptor */
	
	protected PropertyDescriptor getNestedDescriptor(BindingProperty bindingProperty, String name, HashMap<String, PropertyDescriptor> propertyMap, GeneratorBean generatorBean)
	throws GeneratorException
	{
	String propertyName = name;
	int index = name.indexOf(".");
	
		if (index >= 0)
		{
			propertyName = name.substring(0, index);
			name = name.substring(index+1);
		}
		
		PropertyDescriptor descriptor = propertyMap.get(propertyName);
		 
		if (descriptor == null)
		{
		String msg = "Invalid property: "+bindingProperty.getName();
				
			throw new GeneratorException(msg);
		}
		
		if (index < 0)
		{
			return descriptor;
		}
		
		PropertyDescriptor descriptors[] = generatorBean.getPropertyDescriptors(descriptor.getPropertyType().getName(), "");
		
		return getNestedDescriptor(bindingProperty, name, getPropertyMap(descriptors), generatorBean);
		
	} // getNestedDescriptor
	
	/************************************************************/
	/** getPropertyMap */
	
	protected HashMap<String, PropertyDescriptor> getPropertyMap(PropertyDescriptor descriptors[])
	{
	HashMap<String, PropertyDescriptor> propertyMap = new HashMap<String, PropertyDescriptor>();
		
		for (int count=0; count<descriptors.length; count++)
		{
		PropertyDescriptor descriptor = descriptors[count];
			
			propertyMap.put(descriptor.getName(), descriptor);
		}
		
		return propertyMap;
	
	} // getPropertyMap
	
	/************************************************************/
	/** getServiceLabel */
	
	protected String getServiceLabel(CrudService service)
	{
	String label = service.getLabel();
	
		if (!label.equals(""))
		{
			return label;
		}
		
		return service.getName();
		
	} // getServiceLabel
	
	/************************************************************/
	/** getWindowHeight */
	
	protected int getWindowHeight(BindingPropertyType properties[])
	{
	int height = 4*VERTICAL_SPACER;
	
		for (int count=0; count<properties.length; count++)
		{
		BindingPropertyType bindingProperty = properties[count];
    	
    		if (bindingProperty.isExcluded() || !bindingProperty.isVisible())
    		{
    			continue;
    		}
    
    		if (count > 0)
    		{
    			height += VERTICAL_SPACER;
    		}
		}
		
		return height;
		
	} // getWindowHeight
	
	/************************************************************/
	/** isTableService */
	
	protected boolean isTableService(CrudService service)
	{
		return service.getReadListService().isEnabled() || 
			   service.getReadPagedListService().isEnabled();
		
	} // isTableService
	
} // CrudUIGenerator

/************************************************************/


